Fixes #450.
Adds a new `pet-hatch` crate so Hatch-managed virtual environments are
no longer misclassified as plain `Venv` by downstream consumers.
### Implementation
Matches Hatch's actual storage layout from
[`src/hatch/env/virtual.py`](https://github.com/pypa/hatch/blob/master/src/hatch/env/virtual.py):
- **Default storage:**
`<data_dir>/env/virtual/<project_name>/<project_id>/<venv_name>` (3
levels deep, not 2).
- **`HATCH_DATA_DIR`** is honoured and never silently falls back to the
platform default when set.
- **Project-local discovery** via `[tool.hatch.dirs.env].virtual` in
`pyproject.toml` or `[dirs.env]` in `hatch.toml`. Handles the `virtual =
".hatch"` example called out in the issue.
- Hatch locator is inserted **before `Venv`** so it claims envs first.
- `LocatorKind::Hatch` registered as
`RefreshStatePersistence::ConfiguredOnly` (workspace-driven discovery).
### Tests
15 unit tests covering:
- Default-storage layout matches at exactly 3 levels deep; rejects
2-deep and 4-deep prefixes.
- `HATCH_DATA_DIR` semantics — used when set, no fallback to platform
default.
- Project-local discovery via both `pyproject.toml` and `hatch.toml`;
rejected without `dirs.env.virtual` config.
- Per-platform `platformdirs` defaults (Linux/macOS/Windows).
### Replaces #451
This PR replaces #451, which had a fundamental layout bug: it assumed
envs are stored 2 levels deep under `<data_dir>/env/virtual`
(`<project-hash>/<env-name>`). Per Hatch's source, the actual layout is
3 levels deep (`<project_name>/<project_id>/<venv_name>`), so #451 would
fail to detect any real Hatch env in the default storage location.
Hatch environments contain a
pyvenv.cfg, so PET previously classified them as plainVenv. Downstream consumers (e.g. vscode-python-environments) then routed them into the wrong manager collection.Changes
pet-hatchcrate implementing theLocatortrait, modeled onpet-uv/pet-virtualenvwrapper.HATCH_DATA_DIR, then platform defaults matchingplatformdirs.user_data_dir("hatch", appauthor=False)+env/virtual(Linux:$XDG_DATA_HOME/hatch/...or~/.local/share/hatch/...; macOS:~/Library/Application Support/hatch/...; Windows:%LOCALAPPDATA%\hatch\...).try_from()claims a prefix as Hatch when it is either:<storage>/<project-hash>/<env-name>), or.hatch/<env-name>/whose grandparent contains ahatch.tomlor a[tool.hatch]/[tool.hatch.*]section inpyproject.toml.pyvenv.cfgpromptforname; setsproject_pathfor the project-local case.find()walks the default storage dir and the configured workspace.hatch/dirs.HatchtoPythonEnvironmentKindandLocatorKind.crates/pet/src/locators.rs): insertHatchimmediately beforeVenvso it claims first.crates/pet/src/jsonrpc.rs): registerLocatorKind::HatchasRefreshStatePersistence::ConfiguredOnly.Notes for reviewers
<project-name>-<hash>directory name, soproject_pathis leftNonethere. It is populated for the project-local.hatch/case..hatch/discovery requires a Hatch marker file in the workspace to avoid false positives on unrelated.hatchdirectories.pet-uvandpet-poetryclaim envs in their managed directories.